﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Animation.cs" company="">
//   
// </copyright>
// <summary>
//   An animation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

/* 
 * Author: Mikkel Thordal Andersen, Juan Mompean Esteban
 * Created: 17.11.2011
 */
namespace ITU_vs_Zombies.Graphics
{
    using System;
    using System.Diagnostics.Contracts;

    using ITU_vs_Zombies.Movement;

    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;

    /// <summary>
    /// An animation.
    /// </summary>
    [Serializable]
    public class Animation
    {
        // The image representing the collection of images used for animation

        // The state of the Animation

        // Width of a given frame
        #region Constants and Fields

        /// <summary>
        /// The position.
        /// </summary>
        public Vector2 Position;

        /// <summary>
        /// The sprite strip.
        /// </summary>
        [NonSerialized]
        public Texture2D SpriteStrip;

        /// <summary>
        /// The color.
        /// </summary>
        private Color color;

        /// <summary>
        /// The current frame.
        /// </summary>
        private int currentFrame;

        /// <summary>
        /// The destination rect.
        /// </summary>
        private Rectangle destinationRect;

        /// <summary>
        /// The elapsed time.
        /// </summary>
        private double elapsedTime;

        /// <summary>
        /// The flip.
        /// </summary>
        private MovementsEnum flip;

        /// <summary>
        /// The frame count.
        /// </summary>
        private int frameCount;

        /// <summary>
        /// The frame time.
        /// </summary>
        private double frameTime;

        /// <summary>
        /// The scale.
        /// </summary>
        private float scale;

        /// <summary>
        /// The source rect.
        /// </summary>
        private Rectangle sourceRect;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a value indicating whether Active.
        /// </summary>
        public bool Active { get; set; }

        /// <summary>
        /// Gets or sets FrameHeight.
        /// </summary>
        public int FrameHeight { get; set; }

        /// <summary>
        /// Gets or sets FrameWidth.
        /// </summary>
        public int FrameWidth { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether IsMoving.
        /// </summary>
        public bool IsMoving { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether Looping. 
        /// Determines if the animation will keep playing or deactivate after one run
        /// </summary>
        public bool Looping { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        public void Dispose()
        {
            if (this.SpriteStrip != null)
            {
                this.SpriteStrip.Dispose();
            }
        }

        /// <summary>
        /// The draw.
        /// </summary>
        /// <param name="spriteBatch">
        /// The sprite Batch.
        /// </param>
        public void Draw(SpriteBatch spriteBatch)
        {
            Contract.Requires(spriteBatch != null);
            SpriteEffects spriteEffects = (this.flip == MovementsEnum.Right)
                                              ? SpriteEffects.None
                                              : SpriteEffects.FlipHorizontally;

            // Only draw the animation when we are active
            if (this.Active)
            {
                spriteBatch.Draw(
                    this.SpriteStrip, 
                    this.destinationRect, 
                    this.sourceRect, 
                    this.color, 
                    0f, 
                    Vector2.Zero, 
                    spriteEffects, 
                    0f);
            }
        }

        /// <summary>
        /// The public method which can flip any texture
        /// </summary>
        /// <param name="movement">
        /// The movement.
        /// </param>
        public void Flip(MovementsEnum movement)
        {
            this.flip = movement;
        }

        /// <summary>
        /// The initialize.
        /// </summary>
        /// <param name="texture">
        /// The texture.
        /// </param>
        /// <param name="position">
        /// The position.
        /// </param>
        /// <param name="frameCount">
        /// The frame Count.
        /// </param>
        /// <param name="color">
        /// The color.
        /// </param>
        /// <param name="scale">
        /// The scale.
        /// </param>
        /// <param name="looping">
        /// The looping.
        /// </param>
        /// <param name="isMoving">
        /// The is Moving.
        /// </param>
        public void Initialize(
            Texture2D texture, Vector2 position, int frameCount, Color color, float scale, bool looping, bool isMoving)
        {
            Contract.Requires(texture != null);

            // Keep a local copy of the values passed in
            this.color = color;
            this.FrameWidth = texture.Width / frameCount;
            this.FrameHeight = texture.Height;
            this.frameCount = frameCount;
            this.frameTime = 500.0 / frameCount;
            this.scale = scale;

            this.Looping = looping;
            this.IsMoving = isMoving;
            this.Position = position;
            this.SpriteStrip = texture;

            // Set the time to zero
            this.elapsedTime = 0;
            this.currentFrame = 0;

            // Set the Animation to active by default
            this.Active = true;

            // Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
            this.sourceRect = new Rectangle(this.currentFrame * this.FrameWidth, 0, this.FrameWidth, this.FrameHeight);

            // Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
            this.destinationRect = new Rectangle(
                (int)this.Position.X, 
                (int)this.Position.Y, 
                (int)(this.FrameWidth * this.scale), 
                (int)(this.FrameHeight * this.scale));

            this.flip = MovementsEnum.Right;
        }

        /// <summary>
        /// The resize.
        /// </summary>
        /// <param name="width">
        /// The width.
        /// </param>
        /// <param name="height">
        /// The height.
        /// </param>
        public void Resize(double width, double height)
        {
            this.destinationRect = new Rectangle(
                this.destinationRect.X, this.destinationRect.Y, (int)width, (int)height);
        }

        /// <summary>
        /// The update.
        /// </summary>
        /// <param name="gameTime">
        /// The game Time.
        /// </param>
        /// Author: Mikkel Thordal Andersen
        public void Update(GameTime gameTime)
        {
            Contract.Requires(gameTime != null);

            // Do not update the game if we are not active
            if (this.Active == false)
            {
                return;
            }

            if (this.IsMoving)
            {
                // Update the elapsed time
                this.elapsedTime += gameTime.ElapsedGameTime.TotalMilliseconds;

                // If the elapsed time is larger than the frame time
                // we need to switch frames
                if (this.elapsedTime > this.frameTime)
                {
                    // Move to the next frame
                    this.currentFrame++;

                    // If the currentFrame is equal to frameCount reset currentFrame to zero
                    if (this.currentFrame == this.frameCount)
                    {
                        this.currentFrame = 0;

                        // If we are not looping deactivate the animation
                        if (this.Looping == false)
                        {
                            this.Active = false;
                        }
                    }

                    // Reset the elapsed time to zero
                    this.elapsedTime = 0;
                }
            }

            // Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
            this.sourceRect = new Rectangle(this.currentFrame * this.FrameWidth, 0, this.FrameWidth, this.FrameHeight);

            // Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
            this.destinationRect = new Rectangle(
                (int)this.Position.X, (int)this.Position.Y, this.destinationRect.Width, this.destinationRect.Height);
        }

        #endregion
    }
}